package com.hejie.apigateway.config;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
@Slf4j
public class AuthGlobalFilter {

    @Value("${jwt.secret}")
    private String jwtSecret;

    @Value("${jwt.token-prefix:Bearer }")
    private String tokenPrefix;

    private static final List<String> WHITE_LIST = List.of(
            // 公开接口白名单
            "/api/users/login",
            "/api/users/register",
            "/api/videos/public/**"
    );

    @Bean
    @Order(-1)
    public GlobalFilter authFilter() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getPath().pathWithinApplication().value();

            // 白名单路径直接放行
            if (isWhiteList(path)) {
                return chain.filter(exchange);
            }

            // 从请求头获取token
            String token = extractToken(request);
            if (!StringUtils.hasText(token)) {
                return unauthorized(exchange, "未提供认证令牌");
            }

            try {
                // 验证JWT令牌
                SecretKey key = Keys.hmacShaKeyFor(jwtSecret.getBytes(StandardCharsets.UTF_8));
                Claims claims = Jwts.parserBuilder()
                        .setSigningKey(key)
                        .build()
                        .parseClaimsJws(token)
                        .getBody();

                // 从令牌中获取用户信息
                String userId = claims.getSubject();
                List<String> roles = claims.get("roles", List.class);
                if (roles == null) {
                    roles = new ArrayList<>();
                }

                // 创建认证对象
                List<SimpleGrantedAuthority> authorities = roles.stream()
                        .map(role -> new SimpleGrantedAuthority("ROLE_" + role))
                        .collect(Collectors.toList());

                Authentication authentication = new UsernamePasswordAuthenticationToken(
                        userId, null, authorities);

                // 将认证信息设置到安全上下文中
                return chain.filter(exchange)
                        .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authentication))
                        .then(Mono.fromRunnable(() -> {
                            // 将用户ID添加到请求头，传递给下游服务
                            ServerHttpRequest mutatedRequest = request.mutate()
                                    .header("X-User-ID", userId)
                                    .build();
                            exchange.mutate().request(mutatedRequest).build();
                        }));

            } catch (Exception e) {
                log.error("JWT令牌验证失败: {}", e.getMessage());
                return unauthorized(exchange, "无效的认证令牌");
            }
        };
    }

    /**
     * 判断路径是否在白名单中
     */
    private boolean isWhiteList(String path) {
        return WHITE_LIST.stream().anyMatch(whitePath -> {
            if (whitePath.endsWith("**")) {
                String prefix = whitePath.substring(0, whitePath.length() - 2);
                return path.startsWith(prefix);
            } else {
                return path.equals(whitePath);
            }
        });
    }

    /**
     * 从请求头中提取JWT令牌
     */
    private String extractToken(ServerHttpRequest request) {
        String bearerToken = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(tokenPrefix)) {
            return bearerToken.substring(tokenPrefix.length());
        }
        return null;
    }

    /**
     * 返回未授权响应
     */
    private Mono<Void> unauthorized(ServerWebExchange exchange, String message) {
        ServerHttpResponse response = exchange.getResponse();
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String body = String.format("{\"code\":401,\"message\":\"%s\"}", message);
        return response.writeWith(Mono.just(response.bufferFactory().wrap(body.getBytes())));
    }
}